﻿using System;
using System.Threading;
using PacketDotNet;

namespace Bouyei.NetworkSniffer
{
    public class ProcessingBuffer
    {
        RawPacketQueue packetQueue = null;
        Timer timingGetPacketThead = null;
        bool isParsing = false;
        LinkFilter linkFilter = new LinkFilter();

        public CaptureCommDataEventArgs CaptureCommDataEventHandler { get; set; }

        public ProcessingBuffer()
        {
            packetQueue = new RawPacketQueue();
            timingGetPacketThead = new Timer(new TimerCallback(ProcessingPacket), null, -1, -1);
        }

        ~ProcessingBuffer()
        {
            timingGetPacketThead.Dispose();
        }

        public void InPacketQueue(RawPacket rawPacket)
        {
            packetQueue.AddQueue(rawPacket);
        }

        public RawPacket OutPacketQueue()
        {
           return packetQueue.RemoveQueue();
        }

        public void StopProcessing()
        {
            timingGetPacketThead.Change(-1, -1);
        }

        public void StartProcessing()
        {
            timingGetPacketThead.Change(3000, 3000);
        }

        public void SetFilter(LinkFilter linkFilter)
        {
            if (linkFilter == null) linkFilter = new LinkFilter();
            else this.linkFilter = linkFilter;
        }

        private void ProcessingPacket(object obj)
        {
            if (packetQueue.Count <= 0) return;
            if (isParsing) return;

            isParsing = true;
            try
            {
                while (packetQueue.Count > 0)
                {
                    RawPacket rawp = packetQueue.RemoveQueue();
                    if (CaptureCommDataEventHandler == null) return;

                    if (rawp != null)
                    {
                        switch (rawp.rawCaptureInfo.LinkLayerType)
                        {
                            case LinkLayers.Ethernet:
                                {
                                    if (!linkFilter.IsEthernet) return;

                                    EthernetPacket ethernet =EthernetPacket.ParsePacket(LinkLayers.Ethernet,
                                        rawp.rawCaptureInfo.Data) as EthernetPacket;

                                    if (ethernet != null)
                                    {
                                        ProcessingEthernet(ethernet);
                                    }
                                }
                                break;
                            case LinkLayers.Ieee80211:
                                {
                                    if (!linkFilter.IsIeee) return;
                                    Ieee8021QPacket ieeePacket = EthernetPacket.ParsePacket(LinkLayers.Ieee80211,
                                        rawp.rawCaptureInfo.Data) as Ieee8021QPacket;
                                    
                                    if (ieeePacket != null)
                                    {
                                        ProcessingIEEEPacket(ieeePacket);
                                    }
                                }
                                break;
                            case LinkLayers.Ppp:
                                {
                                    if (!linkFilter.IsPPP) return;
                                    PPPPacket pppPacket = EthernetPacket.ParsePacket(LinkLayers.Ppp,
                                        rawp.rawCaptureInfo.Data) as PPPPacket;
                                    if (pppPacket != null)
                                    {
                                        ProcessingPPPPacket(pppPacket);
                                    }
                                }
                                break;
                            default:
                                break;
                        }
                    }
                }
            }
            finally
            {
                isParsing = false;
            }
        }

        #region Ethernet

        private void ProcessingEthernet(EthernetPacket ethernetPacket)
        {
            if (ethernetPacket == null) return;

            UnitPacket cpacket = new UnitPacket()
            {
                SourceMacAddress = ethernetPacket.SourceHwAddress,
                DestinationMacAddress = ethernetPacket.DestinationHwAddress,
                rawBuffer=ethernetPacket.BytesHighPerformance
            };

            if (ethernetPacket.PayloadPacket != null)
            {
                switch (ethernetPacket.Type)
                {
                    case EthernetPacketType.IpV6:
                    case EthernetPacketType.IpV4:
                        {
                            IpPacket ipPacket = ethernetPacket.PayloadPacket as IpPacket;
                            if (ipPacket != null)
                            {
                                ProcessingIPPacket(ipPacket, cpacket);
                            }
                        }
                        break;
                    case EthernetPacketType.Arp:
                        {
                            ARPPacket arpPacket = ethernetPacket.PayloadPacket as ARPPacket;
                            if (arpPacket != null) { 
                            ProcessingARPPacket(arpPacket, cpacket);
                            }
                        }
                        break;
                    case EthernetPacketType.WakeOnLan:
                        {
                            WakeOnLanPacket onlanPacket = ethernetPacket.PayloadPacket as WakeOnLanPacket;
                            if (onlanPacket != null) { 
                             ProcessingWakeOnLanPacket(onlanPacket, cpacket);
                            }
                        }
                        break;
                    case EthernetPacketType.PointToPointProtocolOverEthernetSessionStage:
                        {
                            cpacket.pType = DataPacketType.PPPoE_S;
                            PPPoEPacket pppoePacket = ethernetPacket.PayloadPacket as PPPoEPacket;
                            if (pppoePacket != null)
                            {
                               
                            }
                        }
                      
                        break;
                    case EthernetPacketType.PointToPointProtocolOverEthernetDiscoveryStage:
                        {
                            cpacket.pType = DataPacketType.PPPoE_D;
                            PPPoEPacket pppoePacket = ethernetPacket.PayloadPacket as PPPoEPacket;
                        }
                        break;
                    //case EthernetPacketType.LLDP:
                    //    {
                    //        LLDPPacket lldpPacket = (LLDPPacket)ethernetPacket.PayloadPacket;
                    //        if (lldpPacket == null) return null;
                    //    }
                    // break;
                    default:
                        cpacket.pBuffer = ethernetPacket.BytesHighPerformance.ActualBytes();
                        break;
                }
            }

            CaptureCommDataEventHandler(cpacket);
        }

        private void ProcessingIPPacket(IpPacket ipPacket, UnitPacket cpacket)
        {
            cpacket.SourceAddress = ipPacket.SourceAddress;
            cpacket.DestinationAddress = ipPacket.DestinationAddress;

            if (ipPacket.PayloadPacket != null)
            {
                switch (ipPacket.Protocol)
                {
                    case IPProtocolType.TCP:
                        {
                            cpacket.pType = DataPacketType.Tcp;
                            TcpPacket tcpPacket = ipPacket.PayloadPacket as TcpPacket;
                            if (tcpPacket != null)
                            {
                                ProcessingTCPPacket(tcpPacket, cpacket);
                            }
                        }
                        break;
                    case IPProtocolType.UDP:
                        {
                            cpacket.pType = DataPacketType.Udp;
                            UdpPacket udpPacket = ipPacket.PayloadPacket as UdpPacket;
                            if (udpPacket != null)
                            {
                                ProcessingUDPPacket(udpPacket, cpacket);
                            }
                        }
                        break;
                    case IPProtocolType.ICMP:
                        {
                            cpacket.pType = DataPacketType.Icmp;
                            ICMPv4Packet icmpv4Packet = ipPacket.PayloadPacket as ICMPv4Packet;
                            if (icmpv4Packet != null)
                            {
                                ProcessingICMPPacket(icmpv4Packet, cpacket);
                            }
                        }
                        break;
                    case IPProtocolType.ICMPV6:
                        {
                            cpacket.pType = DataPacketType.Icmp;
                            ICMPv6Packet icmpv6Packet = ipPacket.PayloadPacket as ICMPv6Packet;
                            if (icmpv6Packet != null)
                            {
                                ProcessingICMPV6Packet(icmpv6Packet, cpacket);
                            }
                        }
                        break;
                    case IPProtocolType.IGMP:
                        {
                            cpacket.pType = DataPacketType.Igmp;

                            IGMPv2Packet igmpv2Packet = ipPacket.PayloadPacket as IGMPv2Packet;
                            if (igmpv2Packet != null)
                            {
                                ProcessingIGMPPacket(igmpv2Packet, cpacket);
                            }
                        }
                        break;
                    default:
                        {
                            cpacket.pBuffer = ipPacket.BytesHighPerformance.ActualBytes();
                        }
                        break;
                }
            }

            CaptureCommDataEventHandler(cpacket);
        }

        private void ProcessingARPPacket(ARPPacket arpPacket, UnitPacket cpacket)
        {
            cpacket.SourceAddress = arpPacket.SenderProtocolAddress;
            cpacket.DestinationAddress = arpPacket.TargetProtocolAddress;
            cpacket.SourceMacAddress = arpPacket.SenderHardwareAddress;
            cpacket.DestinationMacAddress = arpPacket.TargetHardwareAddress;
            cpacket.pBuffer = arpPacket.PayloadData;
            cpacket.pType = DataPacketType.Arp;

            CaptureCommDataEventHandler(cpacket);

            if (arpPacket.PayloadPacket == null || !(arpPacket.PayloadPacket is ARPPacket)) return;

             ProcessingARPPacket(arpPacket.PayloadPacket as ARPPacket, cpacket);
        }

        private void ProcessingWakeOnLanPacket(WakeOnLanPacket wakeOnLanPacket, UnitPacket cpacket)
        {
            cpacket.DestinationMacAddress = wakeOnLanPacket.DestinationMAC;
            cpacket.pBuffer = wakeOnLanPacket.PayloadData;
            cpacket.pType = DataPacketType.WOL;

            CaptureCommDataEventHandler(cpacket);

            if (wakeOnLanPacket.PayloadPacket == null || !(wakeOnLanPacket.PayloadPacket is WakeOnLanPacket)) return;

            ProcessingWakeOnLanPacket(wakeOnLanPacket.PayloadPacket as WakeOnLanPacket, cpacket);
        }

        private void ProcessingTCPPacket(TcpPacket tcpPacket, UnitPacket cpacket)
        {
            cpacket.SourcePort = tcpPacket.SourcePort;
            cpacket.DestinactionPort = tcpPacket.DestinationPort;
            cpacket.pBuffer = tcpPacket.PayloadData;
            cpacket.pType = DataPacketType.Tcp;

            CaptureCommDataEventHandler(cpacket);

            if (tcpPacket.PayloadPacket == null || !(tcpPacket.PayloadPacket is TcpPacket)) return;

             ProcessingTCPPacket(tcpPacket.PayloadPacket as TcpPacket, cpacket);
        }

        private void ProcessingUDPPacket(UdpPacket udpPacket, UnitPacket cpacket)
        {
            cpacket.SourcePort = udpPacket.SourcePort;
            cpacket.DestinactionPort = udpPacket.DestinationPort;
            cpacket.pBuffer = udpPacket.PayloadData;
            cpacket.pType = DataPacketType.Udp;

            CaptureCommDataEventHandler(cpacket);

            if (udpPacket.PayloadPacket == null ||!(udpPacket.PayloadPacket is UdpPacket)) return;

           ProcessingUDPPacket(udpPacket.PayloadPacket as UdpPacket, cpacket);
        }

        private void ProcessingICMPPacket(ICMPv4Packet icmpv4Packet, UnitPacket cpacket)
        {
            cpacket.pBuffer = icmpv4Packet.PayloadData;
            cpacket.pType = DataPacketType.Icmp;

            CaptureCommDataEventHandler(cpacket);

            if (icmpv4Packet.PayloadPacket == null || !(icmpv4Packet is ICMPv4Packet)) return;

            ProcessingICMPPacket(icmpv4Packet.PayloadPacket as ICMPv4Packet, cpacket);           
        }

        private void ProcessingICMPV6Packet(ICMPv6Packet icmpv6Packet, UnitPacket cpacket)
        {
            cpacket.pBuffer = icmpv6Packet.PayloadData;
            cpacket.pType = DataPacketType.Icmpv6;
            CaptureCommDataEventHandler(cpacket);

            if (icmpv6Packet.PayloadPacket == null ||!(icmpv6Packet.PayloadPacket is ICMPv6Packet)) return;

             ProcessingICMPV6Packet(icmpv6Packet.PayloadPacket as ICMPv6Packet, cpacket);   
        }

        private void ProcessingIGMPPacket(IGMPv2Packet igmpPacket, UnitPacket cpacket)
        {
            cpacket.pBuffer = igmpPacket.PayloadData;
            cpacket.SourceAddress = igmpPacket.GroupAddress;
            cpacket.pType = DataPacketType.Igmp;
            CaptureCommDataEventHandler(cpacket);

            if (igmpPacket.PayloadPacket == null ||!(igmpPacket is IGMPv2Packet)) return;

             ProcessingIGMPPacket(igmpPacket.PayloadPacket as IGMPv2Packet, cpacket);   
        }

        #endregion 

        #region IEEE80211

        private void ProcessingIEEEPacket(Ieee8021QPacket ieeePacket)
        {
            UnitPacket cpacket = new UnitPacket()
            {
                pType = DataPacketType.IEEE,
                pBuffer = ieeePacket.PayloadData,
                rawBuffer=ieeePacket.BytesHighPerformance
            };

            CaptureCommDataEventHandler(cpacket);

            if (ieeePacket.PayloadPacket == null ||!(ieeePacket.PayloadPacket is Ieee8021QPacket)) return;

             ProcessingIEEEPacket(ieeePacket.PayloadPacket as Ieee8021QPacket);
        }

        #endregion

        #region PPP

        private void ProcessingPPPPacket(PPPPacket pppPacket)
        {
            UnitPacket cpacket = new UnitPacket()
            {
                pType = DataPacketType.PPP,
                pBuffer = pppPacket.PayloadData,
                rawBuffer=pppPacket.BytesHighPerformance
            };
           
            CaptureCommDataEventHandler(cpacket);

            if (pppPacket.PayloadPacket == null || !(pppPacket is PPPPacket)) return;

            ProcessingPPPPacket(pppPacket.PayloadPacket as PPPPacket);
        }

        #endregion
    }
}
